﻿using DotNetCommon.Extensions;
using System;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace DotNetCommon
{
    #region MD5
    /// <summary>
    /// Message Digest Algorithmn，一种被广泛使用的密码散列函数，可以产生出一个 128 bit 的散列值（hash value），用于确保信息传输完整一致。<br/>
    /// 参考: <seealso href="https://www.toolhelper.cn/DigestAlgorithm/Hash"/>
    /// <list type="bullet">
    /// <item>Hash-16位 生成的结果：64 bit，8 byte</item>
    /// <item>Hash-32位 生成的结果：128 bit，16 byte</item>
    /// </list>
    /// </summary>
    /// <remarks>注意: 这里用 32 位</remarks>
    public static class MD5Helper
    {
        private static EasyPool<MD5> pool = new EasyPool<MD5>(() => System.Security.Cryptography.MD5.Create(), s => s.Initialize(), 200);

        /// <summary>
        /// md5 hash 计算, 调用: System.Security.Cryptography.MD5.ComputeHash()
        /// </summary>
        public static byte[] Hash(byte[] bytes)
        {
            if (bytes.IsNullOrEmpty()) return bytes;
            var sha = pool.Get();
            try
            {
                return sha.ComputeHash(bytes);
            }
            finally
            {
                pool.Return(sha);
            }
        }

        /// <summary>
        /// 步骤: 
        /// <list type="bullet">
        /// <item>获取字符串编码;</item>
        /// <item>使用 Hash.ComputeHash() 得到结果 byte[];</item>
        /// <item>对结果 byte[] 转成16进制字符串</item>
        /// </list>
        /// 参数示例:
        /// <list type="bullet">
        /// <item>sourceEncode 示例: unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>outMode 示例: Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static string Hash(string source, string sourceEncode = "utf-8", string outMode = "Hex")
        {
            if (source == null) return source;
            LuanMaHelper.InitGBK();
            byte[] bytes = Encoding.GetEncoding(sourceEncode).GetBytes(source);
            var hashBytes = Hash(bytes);
            var s = outMode.ToLower() switch
            {
                "base64urlsafe" => Base64UrlSafe.Encode(hashBytes),
                "base64" => Convert.ToBase64String(hashBytes),
                "hex" => BytesToHexConverter.ToHex(hashBytes),
                _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            Array.Clear(bytes);
            Array.Clear(hashBytes);
            return s;
        }

        /// <summary>
        /// Hash hash流
        /// <list type="bullet">
        /// <item>outMode 示例: Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        /// <remarks>注意: stream 的指针是否需要在开始位置,如果需要,调用: <c>stream.Seek(0, SeekOrigin.Begin);</c></remarks>
        public static async Task<string> HashAsync(Stream stream, string outMode = "Hex")
        {
            if (stream == null) throw new Exception("流不能为null!");
            var md5 = pool.Get();
            try
            {
                var hashBytes = await md5.ComputeHashAsync(stream);
                var s = outMode.ToLower() switch
                {
                    "base64urlsafe" => Base64UrlSafe.Encode(hashBytes),
                    "base64" => Convert.ToBase64String(hashBytes),
                    "hex" => BytesToHexConverter.ToHex(hashBytes),
                    _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
                };
                Array.Clear(hashBytes);
                return s;
            }
            finally
            {
                pool.Return(md5);
            }
        }

        /// <summary>
        /// Hash hash文件
        /// <list type="bullet">
        /// <item>outMode 示例: Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static async Task<string> HashFileAsync(string absPath, string outMode = "Hex")
        {
            if (absPath.IsNullOrEmptyOrWhiteSpace()) return absPath;
            using var stream = new FileStream(absPath, FileMode.Open);
            return await HashAsync(stream, outMode);
        }
    }
    #endregion

    #region SHA256
    /// <summary>
    /// Secure Hash Algorithm，安全哈希算法主要适用于数字签名标准里面定义的数字签名算法。<br />
    /// 参考: <seealso href="https://www.toolhelper.cn/DigestAlgorithm/SHA"/>
    /// <list type="bullet">
    /// <item>SHA1 生成的结果为：160 bit，20 byte</item>
    /// <item>SHA256 生成的结果为：256 bit，32 byte</item>
    /// <item>SHA384 生成的结果为：384 bit，48 byte</item>
    /// <item>SHA512 生成的结果为：512 bit，64 byte</item>
    /// </list>
    /// </summary>
    /// <remarks>注意: 这里用 SHA256</remarks>
    public static class SHA256Helper
    {
        private static EasyPool<SHA256> pool = new EasyPool<SHA256>(() => SHA256.Create(), s => s.Initialize(), 200);

        /// <summary>
        /// sha256 hash 计算, 调用: System.Security.Cryptography.SHA256.ComputeHash()
        /// </summary>
        public static byte[] Hash(byte[] bytes)
        {
            if (bytes.IsNullOrEmpty()) return bytes;
            var sha = pool.Get();
            try
            {
                return sha.ComputeHash(bytes);
            }
            finally
            {
                pool.Return(sha);
            }
        }

        /// <summary>
        /// 步骤: 
        /// <list type="bullet">
        /// <item>获取字符串编码;</item>
        /// <item>使用 SHA256.ComputeHash() 得到结果 byte[];</item>
        /// <item>对结果 byte[] 再进行 <seealso cref="Base64UrlSafe.Encode(byte[])"/> 编码</item>
        /// </list>
        /// 参数示例:
        /// <list type="bullet">
        /// <item>sourceEncode 示例: unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>outMode 示例: Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static string Hash(string source, string sourceEncode = "utf-8", string outMode = "Base64UrlSafe")
        {
            if (source == null) return source;
            LuanMaHelper.InitGBK();
            byte[] bytes = Encoding.GetEncoding(sourceEncode).GetBytes(source);
            var hashBytes = Hash(bytes);
            var s = outMode.ToLower() switch
            {
                "base64urlsafe" => Base64UrlSafe.Encode(hashBytes),
                "base64" => Convert.ToBase64String(hashBytes),
                "hex" => BytesToHexConverter.ToHex(hashBytes),
                _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            Array.Clear(bytes);
            Array.Clear(hashBytes);
            return s;
        }

        /// <summary>
        /// Sha256 hash流
        /// <list type="bullet">
        /// <item>outMode 示例: Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        /// <remarks>注意: stream 的指针是否需要在开始位置,如果需要,调用: <c>stream.Seek(0, SeekOrigin.Begin);</c></remarks>
        public static async Task<string> HashAsync(Stream stream, string outMode = "Base64UrlSafe")
        {
            if (stream == null) throw new Exception("流不能为null!");
            var sha = pool.Get();
            try
            {
                var hashBytes = await sha.ComputeHashAsync(stream);
                var s = outMode.ToLower() switch
                {
                    "base64urlsafe" => Base64UrlSafe.Encode(hashBytes),
                    "base64" => Convert.ToBase64String(hashBytes),
                    "hex" => BytesToHexConverter.ToHex(hashBytes),
                    _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
                };
                Array.Clear(hashBytes);
                return s;
            }
            finally
            {
                pool.Return(sha);
            }
        }

        /// <summary>
        /// Sha256 hash文件
        /// <list type="bullet">
        /// <item>outMode 示例: Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static async Task<string> HashFileAsync(string absPath, string outMode = "Base64UrlSafe")
        {
            if (absPath.IsNullOrEmptyOrWhiteSpace()) return absPath;
            using var stream = new FileStream(absPath, FileMode.Open);
            return await HashAsync(stream, outMode);
        }
    }
    #endregion

    #region DES
    /// <summary>
    /// Data Encryption Standard，是一种对称加密算法，用于数据的加密和解密。<br />
    /// <list type="bullet">
    /// <item>明文按 64 位进行分组，密钥长 64 位(8字节)</item>
    /// <item>运算模式支持: ECB/CBC/CFB</item>
    /// <item>填充模式支持: None/Zeros/PKCS7/ISO10126/ANSIX923</item>
    /// </list>
    /// 参考: <seealso href="https://www.toolhelper.cn/SymmetricEncryption/DES"/>
    /// </summary>
    public static class DESHelper
    {
        private static CipherMode getMode(string mode)
        {
            switch (mode.ToUpper())
            {
                case "ECB": return CipherMode.ECB;
                case "CBC": return CipherMode.CBC;
                case "CFB": return CipherMode.CFB;
            }
            throw new Exception("不支持的运算模式,请选择其中一个: ECB/CBC/CFB !");
        }
        private static PaddingMode getPadding(string padding)
        {
            switch (padding.ToUpper())
            {
                case "NONE": return PaddingMode.None;
                case "ZEROS": return PaddingMode.Zeros;
                case "PKCS7": return PaddingMode.PKCS7;
                case "ISO10126": return PaddingMode.ISO10126;
                case "ANSIX923": return PaddingMode.ANSIX923;
            }
            throw new Exception("不支持的填充模式,请选择其中一个: None/Zeros/PKCS7/ISO10126/ANSIX923 !");
        }

        private static void validateLength(byte[] arr, string type)
        {
            if (arr.IsNullOrEmpty() || (arr.Length != 8))
                throw new Exception($"{type}的长度必须为8,比如: \"12345678\" 是8个字节,是有效的!");
        }

        /// <summary>
        /// 加密字节数组，参数:<br/>
        /// <list type="bullet">
        /// <item>srcData: 源数据</item>
        /// <item>key: 密码,必须是8个字节</item>
        /// <item>iv: 偏移量(也是8个字节), 如果 mode = ECB, 则不需要设置</item>
        /// <item>mode: 运算模式, 支持 ECB/CBC/CFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static byte[] Encrypt(byte[] srcData, byte[] key, byte[] iv = null, string mode = "ECB", string padding = "pkcs7")
        {
            if (srcData == null) return null;
            validateLength(key, "秘钥");
            using var des = DES.Create();
            des.KeySize = 64;
            des.Key = key;
            des.Mode = getMode(mode);
            des.Padding = getPadding(padding);
            if (des.Padding == PaddingMode.None)
            {
                if (srcData.Length % 8 != 0) throw new Exception($"采用 PaddingMode.None 需要保证原始字数据长度是8的倍数,如: byte[8]");
            }
            //https://github.com/dotnet/runtime/issues/85079
            if (des.Mode == CipherMode.CFB) des.FeedbackSize = 8;
            //ecb不需要iv, cbc/cfb 则必须
            if (des.Mode != CipherMode.ECB)
            {
                iv = iv ?? key;
                validateLength(iv, "偏移量");
                des.IV = iv ?? key;
            }
            using var mStream = new MemoryStream();
            using var cStream = new CryptoStream(mStream, des.CreateEncryptor(), CryptoStreamMode.Write);
            cStream.Write(srcData, 0, srcData.Length);
            cStream.FlushFinalBlock();
            return mStream.ToArray();
        }

        /// <summary>
        /// 解密字节数组，参数:<br/>
        /// <list type="bullet">
        /// <item>srcData: 源数据</item>
        /// <item>key: 密码,必须是8个字节</item>
        /// <item>iv: 偏移量(也是8个字节), 如果 mode = ECB, 则不需要设置</item>
        /// <item>mode: 运算模式, 支持 ECB/CBC/CFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static byte[] Decrypt(byte[] srcData, byte[] key, byte[] iv = null, string mode = "ECB", string padding = "pkcs7")
        {
            if (srcData == null) return null;
            validateLength(key, "秘钥");
            using var des = DES.Create();
            des.KeySize = 64;
            des.Key = key;
            des.Mode = getMode(mode);
            des.Padding = getPadding(padding);
            //https://github.com/dotnet/runtime/issues/85079
            if (des.Mode == CipherMode.CFB) des.FeedbackSize = 8;
            //ecb不需要iv, cbc/cfb 则必须
            if (des.Mode != CipherMode.ECB)
            {
                iv = iv ?? key;
                validateLength(iv, "偏移量");
                des.IV = iv ?? key;
            }
            using var mStream = new MemoryStream();
            using var cStream = new CryptoStream(mStream, des.CreateDecryptor(), CryptoStreamMode.Write);
            cStream.Write(srcData, 0, srcData.Length);
            cStream.FlushFinalBlock();
            mStream.Seek(0, SeekOrigin.Begin);
            return mStream.ToArray();
        }

        /// <summary>
        /// 加密字符串，参数:<br/>
        /// <list type="bullet">
        /// <item>source: 源字符串</item>
        /// <item>key: 密码,必须是8个字节</item>
        /// <item>sourceEncode: 源字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>iv: 偏移量(也是8个字节), 如果 mode = ECB, 则不需要设置</item>
        /// <item>mode: 运算模式, 支持 ECB/CBC/CFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// <item>outMode: 输出格式, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static string Encrypt(string source, string key, string sourceEncode = "utf-8", string mode = "ECB", string padding = "pkcs7", string iv = null, string outMode = "Hex")
        {
            if (source == null) return source;
            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);

            LuanMaHelper.InitGBK();
            byte[] bytes = Encoding.GetEncoding(sourceEncode).GetBytes(source);

            var ret = Encrypt(bytes, keyArr, ivArr, mode, padding);
            var s = outMode.ToLower() switch
            {
                "base64urlsafe" => Base64UrlSafe.Encode(ret),
                "base64" => Convert.ToBase64String(ret),
                "hex" => BytesToHexConverter.ToHex(ret),
                _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            Array.Clear(bytes);
            Array.Clear(ret);
            return s;
        }

        /// <summary>
        /// 解密字符串，参数:<br/>
        /// <list type="bullet">
        /// <item>source: 加密后的字符串</item>
        /// <item>key: 密码,必须是8个字节</item>
        /// <item>sourceMode: 加密模式, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// <item>outEncode: 输出字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>iv: 偏移量(也是8个字节), 如果 mode = ECB, 则不需要设置</item>
        /// <item>mode: 运算模式, 支持 ECB/CBC/CFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static string Decrypt(string source, string key, string sourceMode = "Hex", string mode = "ECB", string padding = "pkcs7", string iv = null, string outEncode = "utf-8")
        {
            if (source == null) return source;
            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);

            byte[] src = sourceMode?.ToLower() switch
            {
                "hex" => BytesToHexConverter.FromHex(source.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? source.Substring(2) : source),
                "base64" => Convert.FromBase64String(source),
                "base64urlsafe" => Base64UrlSafe.Decode(source),
                _ => throw new Exception("无效的源格式,有效的参考: Base64UrlSafe/Base64/Hex")
            };

            var ret = Decrypt(src, keyArr, ivArr, mode, padding);

            LuanMaHelper.InitGBK();
            var str = Encoding.GetEncoding(outEncode).GetString(ret);
            //填充0的需要在末尾去掉
            if (string.Equals(padding, "Zeros", StringComparison.OrdinalIgnoreCase)) str = str.Trim('\0');

            Array.Clear(src);
            Array.Clear(ret);
            return str;
        }

        /// <summary>
        /// 加密流，参数:<br/>
        /// <list type="bullet">
        /// <item>stream: 源流</item>
        /// <item>key: 密码,必须是8个字节</item>
        /// <item>outputStream: 输出流</item>
        /// <item>iv: 偏移量(也是8个字节), 如果 mode = ECB, 则不需要设置</item>
        /// <item>mode: 运算模式, 支持 ECB/CBC/CFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        /// <remarks>
        /// 注意: <br/>
        /// <list type="bullet">
        /// <item>stream 的指针是否需要在开始位置,如果需要,调用: <c>stream.Seek(0, SeekOrigin.Begin);</c></item>
        /// <item>outputStream 的指针位置已前进(按需回拨), 且用完后自己记得释放</item>
        /// </list>
        /// </remarks>
        public static async Task EncryptAsync(Stream stream, string key, Stream outputStream, string mode = "ECB", string padding = "pkcs7", string iv = null)
        {
            if (stream == null || outputStream == null) throw new Exception("流不能为空!");

            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            validateLength(keyArr, "秘钥");
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);
            using var des = DES.Create();
            des.KeySize = 64;
            des.Key = keyArr;
            des.Mode = getMode(mode);
            des.Padding = getPadding(padding);
            //https://github.com/dotnet/runtime/issues/85079
            if (des.Mode == CipherMode.CFB) des.FeedbackSize = 8;
            //ecb不需要iv, cbc/cfb 则必须
            if (des.Mode != CipherMode.ECB)
            {
                ivArr = ivArr ?? keyArr;
                validateLength(ivArr, "偏移量");
                des.IV = ivArr;
            }
            using var cStream = new CryptoStream(outputStream, des.CreateEncryptor(), CryptoStreamMode.Write, true);
            var bs = new byte[1024];
            var len = 0;
            while ((len = await stream.ReadAsync(bs, 0, bs.Length)) > 0)
            {
                await cStream.WriteAsync(bs, 0, len);
            }
            cStream.FlushFinalBlock();
        }

        /// <summary>
        /// 解密流，参数:<br/>
        /// <list type="bullet">
        /// <item>stream: 已经加密的流</item>
        /// <item>key: 密码,必须是8个字节</item>
        /// <item>outputStream: 输出流</item>
        /// <item>iv: 偏移量(也是8个字节), 如果 mode = ECB, 则不需要设置</item>
        /// <item>mode: 运算模式, 支持 ECB/CBC/CFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        /// <remarks>
        /// 注意: <br/>
        /// <list type="bullet">
        /// <item>stream 的指针是否需要在开始位置,如果需要,调用: <c>stream.Seek(0, SeekOrigin.Begin);</c></item>
        /// <item>outputStream 的指针位置已前进(按需回拨), 且用完后自己记得释放</item>
        /// </list>
        /// </remarks>
        public static async Task DecryptAsync(Stream stream, string key, Stream outputStream, string mode = "ECB", string padding = "pkcs7", string iv = null)
        {
            if (stream == null || outputStream == null) throw new Exception("流不能为空!");

            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            validateLength(keyArr, "秘钥");
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);
            using var des = DES.Create();
            des.KeySize = 64;
            des.Key = keyArr;
            if (ivArr != null) des.IV = ivArr;
            des.Mode = getMode(mode);
            des.Padding = getPadding(padding);
            //https://github.com/dotnet/runtime/issues/85079
            if (des.Mode == CipherMode.CFB) des.FeedbackSize = 8;
            //ecb不需要iv, cbc/cfb 则必须
            if (des.Mode != CipherMode.ECB)
            {
                ivArr = ivArr ?? keyArr;
                validateLength(ivArr, "偏移量");
                des.IV = ivArr;
            }
            using var cStream = new CryptoStream(outputStream, des.CreateDecryptor(), CryptoStreamMode.Write, true);
            var bs = new byte[1024];
            var len = 0;
            while ((len = await stream.ReadAsync(bs, 0, bs.Length)) > 0)
            {
                await cStream.WriteAsync(bs, 0, len);
            }
            cStream.FlushFinalBlock();
        }
    }
    #endregion

    #region AES
    /// <summary>
    /// AES：Advanced Encryption Standard，又称 Rijndael 加密法。<br />
    /// 这个标准用来替代原先的 DES（Data Encryption Standard），比 DES 有更高的安全性。 AES 算法采用固定长度的密钥（128 bits(16字节)、192 bits(24字节) 或256 bits(32字节)）来加密和解密数据块，加密和解密过程都是基于矩阵运算和字节替换等操作进行的。<br/>
    /// 参考: <seealso href="https://www.toolhelper.cn/SymmetricEncryption/AES"/>
    /// </summary>
    public static class AESHelper
    {
        private static CipherMode getMode(string mode)
        {
            switch (mode.ToUpper())
            {
                case "ECB": return CipherMode.ECB;
                case "CBC": return CipherMode.CBC;
                case "CFB": return CipherMode.CFB;
            }
            throw new Exception("不支持的运算模式,请选择其中一个: ECB/CBC/CFB !");
        }
        private static PaddingMode getPadding(string padding)
        {
            switch (padding.ToUpper())
            {
                case "NONE": return PaddingMode.None;
                case "ZEROS": return PaddingMode.Zeros;
                case "PKCS7": return PaddingMode.PKCS7;
                case "ISO10126": return PaddingMode.ISO10126;
                case "ANSIX923": return PaddingMode.ANSIX923;
            }
            throw new Exception("不支持的填充模式,请选择其中一个: None/Zeros/PKCS7/ISO10126/ANSIX923 !");
        }

        private static void validateLength(byte[] arr, string type)
        {
            if (arr.IsNullOrEmpty() || (arr.Length != 16 && arr.Length != 24 && arr.Length != 32))
                throw new Exception($"{type}的长度必须为16/24/32之一,比如: \"1234567812345678\" 是16个字节,是有效的!");
        }

        /// <summary>
        /// 加密字节数组，参数:<br/>
        /// <list type="bullet">
        /// <item>srcData: 源数据</item>
        /// <item>key: 密码,字节数是 16/24/32 之一</item>
        /// <item>iv: 偏移量,字节数是 16/24/32 之一, 如果为null,则取使用key</item>
        /// <item>mode: 运算模式, 支持ECB/CBC/CTS/CFB/OFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static byte[] Encrypt(byte[] srcData, byte[] key, byte[] iv = null, string mode = "ECB", string padding = "pkcs7")
        {
            if (srcData == null) return null;
            validateLength(key, "秘钥");
            using var aes = Aes.Create();
            aes.KeySize = key.Length * 8;
            aes.Key = key;
            aes.Mode = getMode(mode);
            aes.Padding = getPadding(padding);
            if (aes.Padding == PaddingMode.None)
            {
                //参考: https://www.cnblogs.com/lori/p/14210066.html
                if (srcData.Length % 16 != 0) throw new Exception($"采用 PaddingMode.None 需要保证原始字数据长度是 16 的倍数,如: byte[16]");
            }
            //ecb不需要iv, cbc/cfb 则必须
            if (aes.Mode != CipherMode.ECB)
            {
                iv = iv ?? key;
                validateLength(iv, "偏移量");
                aes.IV = iv ?? key;
            }
            using var mStream = new MemoryStream();
            using var cStream = new CryptoStream(mStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
            cStream.Write(srcData, 0, srcData.Length);
            cStream.FlushFinalBlock();
            return mStream.ToArray();
        }

        /// <summary>
        /// 解密字节数组，参数:<br/>
        /// <list type="bullet">
        /// <item>srcData: 源数据</item>
        /// <item>key: 密码,字节数是 16/24/32 之一</item>
        /// <item>iv: 偏移量,字节数是 16/24/32 之一, 如果为null,则取使用key</item>
        /// <item>mode: 运算模式, 支持ECB/CBC/CTS/CFB/OFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static byte[] Decrypt(byte[] srcData, byte[] key, byte[] iv = null, string mode = "ECB", string padding = "pkcs7")
        {
            if (srcData == null) return null;
            validateLength(key, "秘钥");
            using var aes = Aes.Create();
            aes.KeySize = key.Length * 8;
            aes.Key = key;
            aes.Mode = getMode(mode);
            aes.Padding = getPadding(padding);

            //ecb不需要iv, cbc/cfb 则必须
            if (aes.Mode != CipherMode.ECB)
            {
                iv = iv ?? key;
                validateLength(iv, "偏移量");
                aes.IV = iv ?? key;
            }
            using var mStream = new MemoryStream();
            using var cStream = new CryptoStream(mStream, aes.CreateDecryptor(), CryptoStreamMode.Write);
            cStream.Write(srcData, 0, srcData.Length);
            cStream.FlushFinalBlock();
            mStream.Seek(0, SeekOrigin.Begin);
            return mStream.ToArray();
        }

        /// <summary>
        /// 加密字符串，参数:<br/>
        /// <list type="bullet">
        /// <item>source: 源字符串</item>
        /// <item>key: 密码,字节数是 16/24/32 之一</item>
        /// <item>iv: 偏移量,字节数是 16/24/32 之一, 如果为null,则取使用key</item>
        /// <item>sourceEncode: 源字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>mode: 运算模式, 支持ECB/CBC/CTS/CFB/OFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// <item>outMode: 输出格式, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static string Encrypt(string source, string key, string sourceEncode = "utf-8", string mode = "ECB", string padding = "pkcs7", string iv = null, string outMode = "Hex")
        {
            if (source == null) return source;
            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);

            LuanMaHelper.InitGBK();
            byte[] bytes = Encoding.GetEncoding(sourceEncode).GetBytes(source);

            var ret = Encrypt(bytes, keyArr, ivArr, mode, padding);
            var s = outMode.ToLower() switch
            {
                "base64urlsafe" => Base64UrlSafe.Encode(ret),
                "base64" => Convert.ToBase64String(ret),
                "hex" => BytesToHexConverter.ToHex(ret),
                _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            Array.Clear(bytes);
            Array.Clear(ret);
            return s;
        }

        /// <summary>
        /// 解密字符串，参数:<br/>
        /// <list type="bullet">
        /// <item>source: 加密后的字符串</item>
        /// <item>key: 密码,字节数是 16/24/32 之一</item>
        /// <item>iv: 偏移量,字节数是 16/24/32 之一, 如果为null,则取使用key</item>
        /// <item>sourceMode: 加密模式, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// <item>outEncode: 输出字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>mode: 运算模式, 支持ECB/CBC/CTS/CFB/OFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public static string Decrypt(string source, string key, string sourceMode = "Hex", string mode = "ECB", string padding = "pkcs7", string iv = null, string outEncode = "utf-8")
        {
            if (source == null) return source;
            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);

            byte[] src = sourceMode?.ToLower() switch
            {
                "hex" => BytesToHexConverter.FromHex(source.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? source.Substring(2) : source),
                "base64" => Convert.FromBase64String(source),
                "base64urlsafe" => Base64UrlSafe.Decode(source),
                _ => throw new Exception("无效的源格式,有效的参考: Base64UrlSafe/Base64/Hex")
            };

            var ret = Decrypt(src, keyArr, ivArr, mode, padding);

            LuanMaHelper.InitGBK();
            var str = Encoding.GetEncoding(outEncode).GetString(ret);
            //填充0的需要在末尾去掉
            if (string.Equals(padding, "Zeros", StringComparison.OrdinalIgnoreCase)) str = str.Trim('\0');

            Array.Clear(src);
            Array.Clear(ret);
            return str;
        }

        /// <summary>
        /// 加密流，参数:<br/>
        /// <list type="bullet">
        /// <item>stream: 源流</item>
        /// <item>key: 密码,字节数是 16/24/32 之一</item>
        /// <item>iv: 偏移量,字节数是 16/24/32 之一, 如果为null,则取使用key</item>
        /// <item>outputStream: 输出流</item>
        /// <item>mode: 运算模式, 支持ECB/CBC/CTS/CFB/OFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        /// <remarks>
        /// 注意: <br/>
        /// <list type="bullet">
        /// <item>stream 的指针是否需要在开始位置,如果需要,调用: <c>stream.Seek(0, SeekOrigin.Begin);</c></item>
        /// <item>outputStream 的指针位置已前进(按需回拨), 且用完后自己记得释放</item>
        /// </list>
        /// </remarks>
        public static async Task EncryptAsync(Stream stream, string key, Stream outputStream, string mode = "ECB", string padding = "pkcs7", string iv = null)
        {
            if (stream == null || outputStream == null) throw new Exception("流不能为空!");
            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);
            validateLength(keyArr, "秘钥");
            using var aes = Aes.Create();
            aes.KeySize = keyArr.Length * 8;
            aes.Key = keyArr;
            if (iv != null) aes.IV = ivArr;
            aes.Mode = getMode(mode);
            aes.Padding = getPadding(padding);
            //ecb不需要iv, cbc/cfb 则必须
            if (aes.Mode != CipherMode.ECB)
            {
                ivArr = ivArr ?? keyArr;
                validateLength(ivArr, "偏移量");
                aes.IV = ivArr;
            }
            using var cStream = new CryptoStream(outputStream, aes.CreateEncryptor(), CryptoStreamMode.Write, true);
            var bs = new byte[1024];
            var len = 0;
            while ((len = await stream.ReadAsync(bs, 0, bs.Length)) > 0)
            {
                await cStream.WriteAsync(bs, 0, len);
            }
            cStream.FlushFinalBlock();
        }

        /// <summary>
        /// 解密流，参数:<br/>
        /// <list type="bullet">
        /// <item>stream: 已经加密的流</item>
        /// <item>key: 密码,字节数是 16/24/32 之一</item>
        /// <item>iv: 偏移量,字节数是 16/24/32 之一, 如果为null,则取使用key</item>
        /// <item>outputStream: 输出流</item>
        /// <item>mode: 运算模式, 支持ECB/CBC/CTS/CFB/OFB (不区分大小写)</item>
        /// <item>padding: 填充模式, 支持 None/Zeros/PKCS7/ISO10126/ANSIX923 (不区分大小写)</item>
        /// </list>
        /// </summary>
        /// <remarks>
        /// 注意: <br/>
        /// <list type="bullet">
        /// <item>stream 的指针是否需要在开始位置,如果需要,调用: <c>stream.Seek(0, SeekOrigin.Begin);</c></item>
        /// <item>outputStream 的指针位置已前进(按需回拨), 且用完后自己记得释放</item>
        /// </list>
        /// </remarks>
        public static async Task DecryptAsync(Stream stream, string key, Stream outputStream, string mode = "ECB", string padding = "pkcs7", string iv = null)
        {
            if (stream == null || outputStream == null) throw new Exception("流不能为空!");
            byte[] keyArr = Encoding.UTF8.GetBytes(key);
            byte[] ivArr = iv == null ? null : Encoding.UTF8.GetBytes(iv);
            validateLength(keyArr, "秘钥");
            using var aes = Aes.Create();
            aes.KeySize = keyArr.Length * 8;
            aes.Key = keyArr;
            aes.Mode = getMode(mode);
            aes.Padding = getPadding(padding);
            //ecb不需要iv, cbc/cfb 则必须
            if (aes.Mode != CipherMode.ECB)
            {
                ivArr = ivArr ?? keyArr;
                validateLength(ivArr, "偏移量");
                aes.IV = ivArr;
            }
            using var cStream = new CryptoStream(outputStream, aes.CreateDecryptor(), CryptoStreamMode.Write, true);
            var bs = new byte[1024];
            var len = 0;
            while ((len = await stream.ReadAsync(bs, 0, bs.Length)) > 0)
            {
                await cStream.WriteAsync(bs, 0, len);
            }
            cStream.FlushFinalBlock();
        }
    }
    #endregion

    #region RSA
    /// <summary>
    /// RSA：Rivest-Shamir-Adleman 是一种非对称加密算法，由 Ron Rivest、Adi Shamir 和 Leonard Adleman 于 1977 年提出。<br/>
    /// RSA 算法的核心原理是利用两个大素数的乘积作为公钥的一部分，并通过求解大素数的因子来实现加密与解密。具体流程如下：<br/>
    /// <list type="number">
    /// <item>选择两个不同的大素数 p 和 q。</item>
    /// <item>计算 N = p * q，N 被用作模数。</item>
    /// <item>计算欧拉函数 φ(N) = (p - 1) * (q - 1)。</item>
    /// <item>选择一个与 φ(N) 互质的整数 e 作为公钥指数，1 &lt; e &lt; φ(N)。</item>
    /// <item>计算 d，满足 (e * d) mod φ(N) ≡ 1，d 作为私钥指数。</item>
    /// <item>公钥为 (N, e)，私钥为 (N, d)。</item>
    /// <item>加密时，将明文 m 通过公式 c = m^e mod N 进行加密。</item>
    /// <item>解密时，将密文 c 通过公式 m = c^d mod N 进行解密。</item>
    /// </list>
    /// 参考1: <seealso href="https://try8.cn/tool/cipher/rsa"/>
    /// <br/>
    /// 参考2: <seealso href="https://www.toolhelper.cn/AsymmetricEncryption/RsaGenerate"/>
    /// <br/>
    /// 使用示例: <br/>
    /// <code>
    /// RSAHelper.ImportKey("支持 xml/pem").Encrypt("机密数据");
    /// </code>
    /// </summary>
    public class RSAHelper : IDisposable
    {
        private RSA rsa;
        private RSAHelper(RSA rsa)
        {
            this.rsa = rsa;
        }

        #region 生成并导出秘钥对
        /// <summary>
        /// 生成rsa秘钥对,示例:
        /// <code>
        /// //导出xml格式
        /// var (pub, pri) = RSAHelper.Generate().ExportXmlPublicAndPrivate();
        /// 
        /// //导出pem格式
        /// var (pub, pri) = RSAHelper.Generate().ExportPemPkcs8PublicAndPrivate();
        /// </code>
        /// </summary>
        /// <param name="keySize">秘钥长度,1024/2048/4096,一般取2048</param>
        /// <returns></returns>
        public static RSAHelper Generate(int keySize = 2048)
        {
            var rsa = RSACryptoServiceProvider.Create(keySize);
            return new RSAHelper(rsa);
        }

        /// <summary>
        /// 导出xml格式公钥, 返回示例:
        /// <code>
        /// &lt;RSAKeyValue>
        ///    &lt;Modulus>base64...&lt;/Modulus>
        ///    &lt;Exponent>AQAB&lt;/Exponent>
        /// &lt;/RSAKeyValue>
        /// </code>
        /// </summary>
        public string ExportXmlPublic()
        {
            return rsa.ToXmlString(false);
        }

        /// <summary>
        /// 导出xml格式私钥, 返回示例:
        /// <code>
        /// &lt;RSAKeyValue>
        ///     &lt;Modulus>base64...&lt;/Modulus>
        ///     &lt;Exponent>AQAB&lt;/Exponent>
        ///     &lt;P>base64...&lt;/P>
        ///     &lt;Q>base64...&lt;/Q>
        ///     &lt;DP>base64...&lt;/DP>
        ///     &lt;DQ>base64...&lt;/DQ>
        ///     &lt;InverseQ>base64...&lt;/InverseQ>
        ///     &lt;D>base64...&lt;/D>
        /// &lt;/RSAKeyValue>
        /// </code>
        /// </summary>
        public string ExportXmlPrivate()
        {
            return rsa.ToXmlString(true);
        }

        /// <summary>
        /// 导出xml格式公钥私钥, 简化调用: (rsa.ExportXmlPublic(), rsa.ExportXmlPrivate())
        /// </summary>
        public (string pub, string pri) ExportXmlPublicAndPrivate()
        {
            var pri = rsa.ToXmlString(true);
            var pub = rsa.ToXmlString(false);
            return (pub, pri);
        }

        /// <summary>
        /// 导出pem pkcs1 格式公钥, 返回示例:
        /// <code>
        /// -----BEGIN RSA PUBLIC KEY-----<br/>
        /// base64...<br/>
        /// -----END RSA PUBLIC KEY-----<br/>
        /// </code>
        /// </summary>
        public string ExportPemPkcs1Public()
        {
            var pub = rsa.ExportRSAPublicKey();
            var pub_chars = PemEncoding.Write("RSA PUBLIC KEY", pub);
            return new string(pub_chars);
        }

        /// <summary>
        /// 导出pem pkcs1 格式私钥, 返回示例:
        /// <code>
        /// -----BEGIN RSA PRIVATE KEY-----
        /// base64...<br/>
        /// -----END RSA PRIVATE KEY-----
        /// </code>
        /// </summary>
        public string ExportPemPkcs1Private()
        {
            var pri = rsa.ExportRSAPrivateKey();
            var pri_chars = PemEncoding.Write("RSA PRIVATE KEY", pri);
            return new string(pri_chars);
        }

        /// <summary>
        /// 导出pem pkcs1 格式公钥或私钥, 简化调用: (rsa.ExportPemPkcs1Public(), rsa.ExportPemPkcs1Private())
        /// </summary>
        /// <returns></returns>
        public (string pub, string pri) ExportPemPkcs1PublicAndPrivate()
        {

            var pub = ExportPemPkcs1Public();
            var pri = ExportPemPkcs1Private();
            return (pub, pri);
        }

        /// <summary>
        /// 导出pem pkcs8 格式公钥,返回示例:<br/>
        /// <code>
        /// -----BEGIN PUBLIC KEY-----
        /// base64...
        /// -----END PUBLIC KEY-----
        /// </code>
        /// </summary>
        public string ExportPemPkcs8Public()
        {
            var pub = rsa.ExportSubjectPublicKeyInfo();
            var pub_chars = PemEncoding.Write("PUBLIC KEY", pub);
            return new string(pub_chars);
        }

        /// <summary>
        /// 导出pem pkcs8 格式私钥,返回示例:<br/>
        /// <code>
        /// -----BEGIN PRIVATE KEY-----
        /// base64...
        /// -----END PRIVATE KEY-----
        /// </code>
        /// </summary>
        /// <remarks>
        /// 注意: 如果使用密码,则输出示例如:
        /// <code>
        /// -----BEGIN ENCRYPTED PRIVATE KEY-----
        /// base64...
        /// -----END ENCRYPTED PRIVATE KEY-----
        /// </code>
        /// </remarks>
        public string ExportPemPkcs8Private(string password = null)
        {
            if (password.IsNotNullOrEmpty())
            {
                byte[] pri = rsa.ExportEncryptedPkcs8PrivateKey(password, new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 2048));
                var pri_chars = PemEncoding.Write("ENCRYPTED PRIVATE KEY", pri);
                return new string(pri_chars);
            }
            else
            {
                var pri = rsa.ExportPkcs8PrivateKey();
                var pri_chars = PemEncoding.Write("PRIVATE KEY", pri);
                return new string(pri_chars);
            }
        }

        /// <summary>
        /// 导出pem pkcs8 格式公钥或私钥, 简化调用: (rsa.ExportPemPkcs8Public(), rsa.ExportPemPkcs8Private())
        /// </summary>
        /// <param name="password"></param>
        /// <returns></returns>
        public (string pub, string pri) ExportPemPkcs8PublicAndPrivate(string password = null)
        {
            var pub = ExportPemPkcs8Public();
            var pri = ExportPemPkcs8Private(password);
            return (pub, pri);
        }
        #endregion

        #region 读取外部秘钥
        /// <summary>
        /// 导入key,以创建 RsaHelper 实例, 支持导入 xml/pkcs1/pkcs8 公钥/私钥, pkcs8的私钥还支持密码, 导入示例:
        /// <code>
        /// //xml 公钥
        /// &lt;RSAKeyValue>
        ///    &lt;Modulus>base64...&lt;/Modulus>
        ///    &lt;Exponent>AQAB&lt;/Exponent>
        /// &lt;/RSAKeyValue>
        /// 
        /// //xml 私钥
        /// &lt;RSAKeyValue>
        ///     &lt;Modulus>base64...&lt;/Modulus>
        ///     &lt;Exponent>AQAB&lt;/Exponent>
        ///     &lt;P>base64...&lt;/P>
        ///     &lt;Q>base64...&lt;/Q>
        ///     &lt;DP>base64...&lt;/DP>
        ///     &lt;DQ>base64...&lt;/DQ>
        ///     &lt;InverseQ>base64...&lt;/InverseQ>
        ///     &lt;D>base64...&lt;/D>
        /// &lt;/RSAKeyValue>
        /// 
        /// //pem公钥示例 pkcs1
        /// -----BEGIN RSA PUBLIC KEY-----<br/>
        /// base64...<br/>
        /// -----END RSA PUBLIC KEY-----<br/>
        /// </code>
        /// </summary>
        /// <param name="key"></param>
        /// <param name="priPassword">如果私钥有密码,则填入,否则留空</param>
        /// <remarks>
        /// 提示: 私钥中包含了公钥的全部内容
        /// </remarks>
        public static RSAHelper ImportKey(string key, string priPassword = null)
        {
            if (key.IsNullOrEmptyOrWhiteSpace()) throw new Exception($"秘钥不能为空!");
            var rsa = RSACryptoServiceProvider.Create();
            if (key.Contains("<RSAKeyValue>"))
            {
                //xml格式
                rsa.FromXmlString(key);
            }
            else if (priPassword.IsNotNullOrEmptyOrWhiteSpace())
            {
                //pkcs8 加密的私钥
                rsa.ImportFromEncryptedPem(key, priPassword);
            }
            else
            {
                //非加密的秘钥
                rsa.ImportFromPem(key);
            }
            return new RSAHelper(rsa);
        }

        private static byte[] _head = Encoding.ASCII.GetBytes("-----BEGIN CERTIFICATE-----");
        /// <summary>
        /// 导入 x509 的key, "xx.cer" 或 "xx.pfx", 示例:<br/>
        /// <code>
        /// //导入 *.cer
        /// var rsa = RSAHelper.ImportFromX509(File.ReadAllBytes("public-rsa.cer"));
        /// 
        /// //导入 *.pfx
        /// var rsa = RSAHelper.ImportFromX509(File.ReadAllBytes("user-rsa.pfx","123456"));
        /// </code>
        /// </summary>
        public static RSAHelper ImportFromX509(byte[] rawDatas, string password = null)
        {
            if (rawDatas == null) throw new Exception($"参数 rawDatas 不能为空!");
            if (rawDatas.Length < _head.Length) throw new Exception($"参数 rawDatas 太短!");
            var isPublic = true;
            for (var i = 0; i < _head.Length; i++)
            {
                if (rawDatas[i] != _head[i])
                {
                    isPublic = false;
                    break;
                }
            }
            if (isPublic)
            {
                using var x509 = new X509Certificate2(rawDatas);
                var rsa = x509.GetRSAPublicKey();
                return new RSAHelper(rsa);
            }
            else
            {
                using var x509 = new X509Certificate2(rawDatas, password);
                var rsa = x509.GetRSAPrivateKey();
                return new RSAHelper(rsa);
            }
        }
        #endregion

        #region 公钥加密,私钥解密
        /// <summary>
        /// 使用私钥加密
        /// </summary>
        public byte[] Encrypt(byte[] source)
        {
            if (source == null) return source;
            return rsa.Encrypt(source, RSAEncryptionPadding.Pkcs1);
        }

        /// <summary>
        /// 使用私钥加密, 参数:<br />
        /// <list type="bullet">
        /// <item>source: 源字符串</item>
        /// <item>sourceEncode: 源字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>outMode: 输出格式, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// </list>
        /// </summary>
        public string Encrypt(string source, string sourceEncode = "utf-8", string outMode = "base64")
        {
            if (source == null) return source;
            LuanMaHelper.InitGBK();

            byte[] bytes = Encoding.GetEncoding(sourceEncode).GetBytes(source);
            var ret = Encrypt(bytes);
            var s = outMode.ToLower() switch
            {
                "base64urlsafe" => Base64UrlSafe.Encode(ret),
                "base64" => Convert.ToBase64String(ret),
                "hex" => BytesToHexConverter.ToHex(ret),
                _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            Array.Clear(bytes);
            Array.Clear(ret);
            return s;
        }

        /// <summary>
        /// 使用私钥解密
        /// </summary>
        public byte[] Decrypt(byte[] source)
        {
            if (source == null) return source;
            return rsa.Decrypt(source, RSAEncryptionPadding.Pkcs1);
        }

        /// <summary>
        /// 使用私钥解密, 参数:<br />
        /// <list type="bullet">
        /// <item>source: 加密后的字符串</item>
        /// <item>sourceMode: 加密模式, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// <item>outEncode: 输出字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public string Decrypt(string source, string sourceMode = "base64", string outEncode = "utf-8")
        {
            if (source == null) return source;
            byte[] src = sourceMode?.ToLower() switch
            {
                "hex" => BytesToHexConverter.FromHex(source.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? source.Substring(2) : source),
                "base64" => Convert.FromBase64String(source),
                "base64urlsafe" => Base64UrlSafe.Decode(source),
                _ => throw new Exception("无效的源格式,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            var ret = Decrypt(src);
            LuanMaHelper.InitGBK();
            var str = Encoding.GetEncoding(outEncode).GetString(ret);
            Array.Clear(src);
            Array.Clear(ret);
            return str;
        }
        #endregion

        #region 私钥签名,公钥验签
        /// <summary>
        /// 使用私钥签名, hashName支持: SHA1/SHA256/SHA512/MD5
        /// </summary>
        public byte[] Sign(byte[] source, string hashName = "SHA1")
        {
            if (source == null) return source;
            HashAlgorithmName hashAlgorithmName;
            switch (hashName.ToUpper())
            {
                case "SHA1":
                    {
                        hashAlgorithmName = HashAlgorithmName.SHA1;
                        break;
                    }
                case "SHA256":
                    {
                        hashAlgorithmName = HashAlgorithmName.SHA256;
                        break;
                    }
                case "SHA512":
                    {
                        hashAlgorithmName = HashAlgorithmName.SHA512;
                        break;
                    }
                case "MD5":
                    {
                        hashAlgorithmName = HashAlgorithmName.MD5;
                        break;
                    }
                default:
                    throw new Exception($"不支持的hash算法,请选择: SHA1/SHA256/SHA512/MD5 其中之一!");
            }
            return rsa.SignData(source, 0, source.Length, hashAlgorithmName, RSASignaturePadding.Pkcs1);
        }

        /// <summary>
        /// 使用私钥签名, 参数：<br/>
        /// <list type="bullet">
        /// <item>source: 源字符串</item>
        /// <item>sourceEncode: 源字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>outMode: 输出格式, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// <item>hashName: hash算法, 支持 SHA1/SHA256/SHA512/MD5 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public string Sign(string source, string sourceEncode = "utf-8", string outMode = "base64", string hashName = "SHA1")
        {
            if (source == null) return source;
            LuanMaHelper.InitGBK();
            byte[] bytes = Encoding.GetEncoding(sourceEncode).GetBytes(source);
            var ret = Sign(bytes, hashName);
            var s = outMode.ToLower() switch
            {
                "base64urlsafe" => Base64UrlSafe.Encode(ret),
                "base64" => Convert.ToBase64String(ret),
                "hex" => BytesToHexConverter.ToHex(ret),
                _ => throw new Exception("无效的 outMode,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            Array.Clear(bytes);
            Array.Clear(ret);
            return s;
        }

        /// <summary>
        /// 使用公钥验签, 参数: <br/>
        /// <list type="bullet">
        /// <item>source: 源</item>
        /// <item>signature: 针对源的签名</item>
        /// <item>hashName: hash算法, 支持 SHA1/SHA256/SHA512/MD5 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public bool Verify(byte[] source, byte[] signature, string hashName = "SHA1")
        {
            HashAlgorithmName hashAlgorithmName;
            switch (hashName.ToUpper())
            {
                case "SHA1":
                    {
                        hashAlgorithmName = HashAlgorithmName.SHA1;
                        break;
                    }
                case "SHA256":
                    {
                        hashAlgorithmName = HashAlgorithmName.SHA256;
                        break;
                    }
                case "SHA512":
                    {
                        hashAlgorithmName = HashAlgorithmName.SHA512;
                        break;
                    }
                case "MD5":
                    {
                        hashAlgorithmName = HashAlgorithmName.MD5;
                        break;
                    }
                default:
                    throw new Exception($"不支持的hash算法,请选择: SHA1/SHA256/SHA512/MD5 其中之一!");
            }
            return rsa.VerifyData(source, signature, hashAlgorithmName, RSASignaturePadding.Pkcs1);
        }

        /// <summary>
        /// 使用公钥验签, 参数: <br/>
        /// <list type="bullet">
        /// <item>source: 源字符串</item>
        /// <item>sourceEncode: 源字符串使用的编码, 支持 unicode/ascii/utf-8/utf-16/utf-16BE/utf-32/utf-32BE/latin1/gb2312/gbk/gb18030 (不区分大小写)</item>
        /// <item>signature: 针对源字符串的签名</item>
        /// <item>signatureEncode: 签名结果的编码, 支持 Base64UrlSafe/Base64/Hex (不区分大小写)</item>
        /// <item>hashName: hash算法, 支持 SHA1/SHA256/SHA512/MD5 (不区分大小写)</item>
        /// </list>
        /// </summary>
        public bool Verify(string source, string signature, string sourceEncode = "utf-8", string signatureEncode = "base64", string hashName = "SHA1")
        {
            if (source == null) return false;
            LuanMaHelper.InitGBK();

            byte[] bytes = Encoding.GetEncoding(sourceEncode).GetBytes(source);
            byte[] bytes_sig = signatureEncode?.ToLower() switch
            {
                "hex" => BytesToHexConverter.FromHex(signature.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? signature.Substring(2) : signature),
                "base64" => Convert.FromBase64String(signature),
                "base64urlsafe" => Base64UrlSafe.Decode(signature),
                _ => throw new Exception("无效的 signatureEncode 格式,有效的参考: Base64UrlSafe/Base64/Hex")
            };
            return Verify(bytes, bytes_sig, hashName);
        }
        #endregion

        #region 转换格式 xml/pkcs1/pkcs8 互转
        /// <summary>
        /// 判断是否是私钥
        /// </summary>
        public static bool isPrivate(string key)
        {
            if (key.Contains("<P>") || key.Contains("PRIVATE KEY")) return true;
            return false;
        }
        /// <summary>
        /// 将 pem 格式秘钥转为 xml 格式
        /// </summary>
        public static string ConvertToXml(string key, string fromPassword = null)
        {
            using var rsa = RSAHelper.ImportKey(key, fromPassword);
            if (isPrivate(key))
            {
                //私钥
                return rsa.ExportXmlPrivate();
            }
            else
            {
                //公钥
                return rsa.ExportXmlPublic();
            }
        }
        /// <summary>
        /// 将 xml/pkcs8 格式秘钥转为 pem pkcs1 格式
        /// </summary>
        public static string ConvertToPkcs1(string key, string fromPassword = null)
        {
            using var rsa = RSAHelper.ImportKey(key, fromPassword);
            var chars = new char[1024 * 5];
            if (isPrivate(key))
            {
                return rsa.ExportPemPkcs1Private();
            }
            else
            {
                return rsa.ExportPemPkcs1Public();
            }
        }
        /// <summary>
        /// 将 xml/pkcs1 格式秘钥转为 pem pkcs8 格式
        /// </summary>
        public static string ConvertToPkcs8(string key, string fromPassword = null, string toPassword = null)
        {
            using var rsa = RSAHelper.ImportKey(key, fromPassword);
            var chars = new char[1024 * 5];
            if (toPassword.IsNotNullOrEmpty())
            {
                //私钥
                return rsa.ExportPemPkcs8Private(toPassword);
            }
            else
            {
                //公钥或私钥
                if (isPrivate(key))
                {
                    return rsa.ExportPemPkcs8Private(toPassword);
                }
                else
                {
                    return rsa.ExportPemPkcs8Public();
                }
            }
        }
        #endregion

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose() => rsa.Dispose();
    }
    #endregion
}
